# /**
# * Copyright (c) 2022 Beijing Jiaotong University
# * PhotLab is licensed under [Open Source License].
# * You can use this software according to the terms and conditions of the [Open Source License].
# *
# * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
# *
# * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# *
# * See the [Open Source License] for more details.
# */
# /**
# * Author: Shihao Xie
# * Created: Jul. 5, 2022
# * Supported by: National Key Research and Development Program of China
# */
import traceback
from functools import wraps
from inspect import getattr_static

from phot_server.core.exception import to_PhotError
from phot_server.core.main_handler import MainHandler
from phot_server.core.main_processor import MainProcessor
from phot_server.utils.phot_logger import logger


# This decorator is to inject interface function and process function to MainServiceHandler and MainProcessor.
# @param processor_function It is the processor function created by thrift. It would be added to MainProcessor.
def api(processor_function, load_flag=True):
    # @param function It is the interface function defined by developer. It would be added to MainServiceHandler.
    def decorator(function):

        # Doing some work before doing processor function, it is looks like processor_wrapper(processor_function()).
        @wraps(processor_function)
        def processor_wrapper(self, *args, **kwargs):
            return processor_function(self, *args, **kwargs)

        # Doing some work before doing interface function, it is looks like wrapper(function()).
        # Avoid decorator change the function and log the request, response and exception
        @wraps(function)
        def wrapper(self, *args, **kwargs):
            # log the request function name and its parameters
            logger.info(" Request : {}{} ".format(function.__name__, args))

            try:
                response = function(self, *args, **kwargs)
            except Exception as e:
                # turn the exception to PhotError, log the error and throw the error to client
                phot_error = to_PhotError(e)
                logger.error(' {} '.format(phot_error))
                traceback.print_exc()
                raise phot_error

            # log the request function name and its response
            if type(response).__name__ not in ["PhotFile", "PhotFigure"]:
                logger.info(" Response : {} = {} ".format(function.__name__, response))
            else:
                logger.info(" Response : {} = {} ".format(function.__name__, response.name))
            return response

        # To find if MainProcessor had the same processor_function
        # if getattr_static(MainProcessor, processor_function.__name__, None):
        #     msg = 'Error method name REPEAT, \'{}\' has exist'.format(processor_function.__name__)
        #     raise NameError(msg)
        # el
        if load_flag:
            logger.debug("Main processor is loading interface \'{}\'".format(function.__name__))

            # Inject processor_function into MainProcessor
            setattr(MainProcessor, processor_function.__name__, processor_wrapper)

            # Add mapping in MainProcessor
            MainProcessor.add_mapping(MainProcessor, function.__name__, processor_wrapper)

            # Inject function into MainServiceHandler
            setattr(MainHandler, function.__name__, wrapper)
        return function

    return decorator
